#ifndef MDTX_USE_RCPPARMADILLO
#define MDTX_USE_RCPPARMADILLO
#endif

#include <RcppArmadillo.h>
using namespace Rcpp;

#include "mdtx-online/indicator/indicator_type_ordered.h"
using namespace mdtx::online;

#include "indi_common.h"

using minmax_ = indi2<indicator::minmax>;

//' Moving min/max
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return a list of min/max
//' @export
// [[Rcpp::export(rng = false)]]
List minmax(NumericVector x, int period)
{
    auto c = minmax_::create_t(period);
    auto ans = minmax_::run_t(c, x);
    ans.names() = CharacterVector::create("min", "max");
    return ans;
}

// [[Rcpp::export(rng = false)]]
SEXP minmax_new(int period)
{
    return minmax_::create(period);
}

// [[Rcpp::export(rng = false)]]
List minmax_run(SEXP ptr, NumericVector x)
{
    auto ans = minmax_::run(ptr, x);
    ans.names() = CharacterVector::create("min", "min");
    return ans;
}

using argminmax_ = indi2<indicator::argminmax, IntegerVector>;

//' Moving min/max
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return a list of min/max index
//' @export
// [[Rcpp::export(rng = false)]]
List argminmax(NumericVector x, int period)
{
    auto c = argminmax_::create_t(period);
    auto ans = argminmax_::run_t(c, x);
    ans.names() = CharacterVector::create("min", "max");
    return ans;
}

// [[Rcpp::export(rng = false)]]
SEXP argminmax_new(int period)
{
    return argminmax_::create(period);
}

// [[Rcpp::export(rng = false)]]
List argminmax_run(SEXP ptr, NumericVector x)
{
    auto ans = argminmax_::run(ptr, x);
    ans.names() = CharacterVector::create("min", "min");
    return ans;
}

using rollmin_ = indi1<indicator::rollmin>;

//' Moving min
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollmin(NumericVector x, int period)
{
    auto c = rollmin_::create_t(period);
    return rollmin_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollmin_new(int period)
{
    return rollmin_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollmin_run(SEXP ptr, NumericVector x)
{
    return rollmin_::run(ptr, x);
}

using rollmax_ = indi1<indicator::rollmax>;

//' Moving max
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollmax(NumericVector x, int period)
{
    auto c = rollmax_::create_t(period);
    return rollmax_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollmax_new(int period)
{
    return rollmax_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollmax_run(SEXP ptr, NumericVector x)
{
    return rollmax_::run(ptr, x);
}

using rollargmin_ = indi1<indicator::rollargmin, IntegerVector>;

//' Moving argmin
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Integer vector
//' @export
// [[Rcpp::export(rng = false)]]
IntegerVector rollargmin(NumericVector x, int period)
{
    auto c = rollargmin_::create_t(period);
    return rollargmin_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollargmin_new(int period)
{
    return rollargmin_::create(period);
}

// [[Rcpp::export(rng = false)]]
IntegerVector rollargmin_run(SEXP ptr, NumericVector x)
{
    return rollargmin_::run(ptr, x);
}

using rollargmax_ = indi1<indicator::rollargmax, IntegerVector>;

//' Moving argmax
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Integer vector
//' @export
// [[Rcpp::export(rng = false)]]
IntegerVector rollargmax(NumericVector x, int period)
{
    auto c = rollargmax_::create_t(period);
    return rollargmax_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollargmax_new(int period)
{
    return rollargmax_::create(period);
}

// [[Rcpp::export(rng = false)]]
IntegerVector rollargmax_run(SEXP ptr, NumericVector x)
{
    return rollargmax_::run(ptr, x);
}

using rollrank_ = indi1<indicator::rollrank, IntegerVector>;

//' Rolling rank
//'
//' @param x Numeric vector
//' @param period Period of rolling window
//' @return Integer vector
//' @export
// [[Rcpp::export(rng = false)]]
IntegerVector rollrank(NumericVector x, int period)
{
    auto c = rollrank_::create_t(period);
    return rollrank_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollrank_new(int period)
{
    return rollrank_::create(period);
}

// [[Rcpp::export(rng = false)]]
IntegerVector rollrank_run(SEXP ptr, NumericVector x)
{
    return rollrank_::run(ptr, x);
}

using rollmedian_ = indi1<indicator::rollmedian>;

//' Rolling median
//'
//' @param x Numeric vector x
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollmedian(NumericVector x, int period)
{
    auto c = rollmedian_::create_t(period);
    return rollmedian_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollmedian_new(int period)
{
    return rollmedian_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollmedian_run(SEXP ptr, NumericVector x)
{
    return rollmedian_::run(ptr, x);
}

using rollmedian_w_ = indi1<indicator::rollmedian_w>;

//' Rolling weighted median
//'
//' @param x Numeric vector x
//' @param w Numeric vector weight
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollmedian_w(NumericVector price, NumericVector volume, int period)
{
    auto c = rollmedian_w_::create_t(period);
    return rollmedian_w_::run_t(c, price, volume);
}

// [[Rcpp::export(rng = false)]]
SEXP rollmedian_w_new(int period)
{
    return rollmedian_w_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollmedian_w_run(SEXP ptr, NumericVector price, NumericVector volume)
{
    return rollmedian_w_::run(ptr, price, volume);
}

using rollmedian_cu_ = indi1<indicator::rollmedian_cu>;

//' Cumulative median
//'
//' @param x Numeric vector x
//' @param k Integer parameter passed to KLL sketch
//' @return Numeric vector
//' @export
// [[Rcpp::export]]
NumericVector rollmedian_cu(NumericVector x, int k = 200)
{
    auto c = rollmedian_cu_::create_t(k);
    return rollmedian_cu_::run_t(c, x);
}

// [[Rcpp::export]]
SEXP rollmedian_cu_new(int k = 200)
{
    return rollmedian_cu_::create(k);
}

// [[Rcpp::export]]
NumericVector rollmedian_cu_run(SEXP ptr, NumericVector x)
{
    return rollmedian_cu_::run(ptr, x);
}

using rollkth_ = indi1<indicator::rollkth>;

//' Rolling Kth order statistic
//'
//' @param x Numeric vector x
//' @param period Period of rolling window
//' @param k Kth order, starts from 0
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector rollkth(NumericVector x, int period, int k)
{
    auto c = rollkth_::create_t(period, k);
    return rollkth_::run_t(c, x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollkth_new(int period, int k)
{
    return rollkth_::create(period, k);
}

// [[Rcpp::export(rng = false)]]
NumericVector rollkth_run(SEXP ptr, NumericVector x)
{
    return rollkth_::run(ptr, x);
}

class rollbox_
{
private:
    double outlier;
    indicator::rollquantile<3> quantile;

public:
    rollbox_(int period, double outlier = 1.5)
        : outlier(outlier),
          quantile(period, {0.25, 0.5, 0.75})
    {
    }

    List run(NumericVector x)
    {
        R_xlen_t n = x.length();
        NumericVector q1(n), q2(n), q3(n);
        quantile(x.begin(), x.end(), q1.begin(), q2.begin(), q3.begin());
        NumericVector iqr = q3 - q1;
        NumericVector min = q1 - outlier * iqr;
        NumericVector max = q3 + outlier * iqr;
        return List::create(
            Named("min") = min,
            Named("Q1") = q1,
            Named("median") = q2,
            Named("Q3") = q3,
            Named("max") = max);
    }
};

//' Rolling Box and Whisker quatiles
//'
//' @param x Numeric vector x
//' @param period Period of rolling window
//' @param outlier outlier ratio
//' @return List of min, Q1, median, Q3, max
//' @export
// [[Rcpp::export(rng = false)]]
List rollbox(NumericVector x, int period, double outlier = 1.5)
{
    rollbox_ c(period, outlier);
    return c.run(x);
}

// [[Rcpp::export(rng = false)]]
SEXP rollbox_new(int period, double outlier = 1.5)
{
    rollbox_ *ptr = new rollbox_(period, outlier);
    return XPtr<rollbox_>(ptr, true);
}

// [[Rcpp::export(rng = false)]]
List rollbox_run(SEXP ptr, NumericVector x)
{
    XPtr<rollbox_> c(ptr);
    return c->run(x);
}

using aroonosc_ = indi1<indicator::aroonosc>;

//' Aroon oscillator
//'
//' @param high Numeric vector High
//' @param low Numeric vector Low
//' @param period Period of rolling window
//' @return Numeric vector
//' @export
// [[Rcpp::export(rng = false)]]
NumericVector aroonosc(NumericVector high, NumericVector low, int period)
{
    auto c = aroonosc_::create_t(period);
    return aroonosc_::run_t(c, high, low);
}

// [[Rcpp::export(rng = false)]]
SEXP aroonosc_new(int period)
{
    return aroonosc_::create(period);
}

// [[Rcpp::export(rng = false)]]
NumericVector aroonosc_run(SEXP ptr, NumericVector high, NumericVector low)
{
    return aroonosc_::run(ptr, high, low);
}
